package edu.zju.gis.dbfg.queryserver.tool;

import com.carrotsearch.hppc.cursors.ObjectCursor;
import edu.zju.gis.dbfg.queryserver.model.SearchResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.action.admin.indices.analyze.DetailAnalyzeResponse;
import org.elasticsearch.action.admin.indices.get.GetIndexAction;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.*;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.license.GetBasicStatusResponse;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.index.get.GetStats;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 用于操作ES的帮助类
 *
 * @author Chenyr
 * 2019/10/05
 */
@Slf4j
public final class RestElasticSearchHelper implements Closeable {
    private RestClientBuilder builder;
    private RestHighLevelClient client;

    public RestElasticSearchHelper(List<String> hosts, int port) {
        HttpHost[] httpHosts = new HttpHost[hosts.size()];
        for (int i = 0; i < httpHosts.length; i++) {
            httpHosts[i] = new HttpHost(hosts.get(i), port);
        }
        this.builder = RestClient.builder(httpHosts);
        this.client = new RestHighLevelClient(builder);
    }

    public RestElasticSearchHelper(RestClientBuilder builder) {
        this.builder = builder;
        this.client = new RestHighLevelClient(builder);
    }

    /**
     * 刷新当前RestClient，防止长时间运行CPU占用过高（检查：是否有用）
     */
    public synchronized void refresh() {
        try {
            RestHighLevelClient prevClient = client;
            client = new RestHighLevelClient(builder);
            Thread.sleep(1000);
            prevClient.close();
        } catch (IOException | InterruptedException e) {
            log.error("RestElasticSearchHelper.client closed with exception", e);
        } finally {
            log.info("RestElasticSearchHelper refreshed successfully.");
        }
    }

    @Override
    public void close() throws IOException {
        client.close();
    }

    /**
     * 读取集群的健康状态
     */
    public ClusterHealthStatus getClusterHealth() throws IOException {
        return client.cluster().health(new ClusterHealthRequest(), RequestOptions.DEFAULT).getStatus();
    }

    /**
     * 清除Scroll记录
     */
    public boolean ClearScroll(String scrollId) {
        ClearScrollRequest csRt = new ClearScrollRequest();
        csRt.addScrollId(scrollId);
        try {
            client.clearScroll(csRt, RequestOptions.DEFAULT);
            return true;
        } catch (Exception e) {
            System.out.println("Clean Scroll Failed");
        }
        return false;
    }

    /**
     * 获取索引FieldsMapping
     */
    public Map<String, Object> fieldsInfo(String indexName) {
        try {
            GetMappingsRequest request = new GetMappingsRequest();
            request.indices(indexName);
            GetMappingsResponse getMappingResponse = client.indices().getMapping(request, RequestOptions.DEFAULT);
            ObjectCursor<String> key = getMappingResponse.getMappings().get(indexName).keys().iterator().next();
            MappingMetaData field = getMappingResponse.getMappings().get(indexName).get(key.value);
            return field.getSourceAsMap();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取记录总数
     */
    public long selectTotalSize(String indexName) {
        try {
            CountRequest countRequest = new CountRequest();
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());
            countRequest.source(searchSourceBuilder);
            countRequest.indices(indexName);
            CountResponse response = client.count(countRequest, RequestOptions.DEFAULT);
            return response.getCount();
        } catch (Exception e) {
            return -1;
        }
    }

    public SearchResponse search(SearchRequest request) {
        try {
            return client.search(request, RequestOptions.DEFAULT);
        } catch (Exception ex) {
            return null;
        }
    }

    public SearchResponse scroll(SearchScrollRequest request) {
        try {
            return client.scroll(request, RequestOptions.DEFAULT);
        } catch (Exception ex) {
            return null;
        }
    }
}
